<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Fuzzy Inference System for Washing Machine</title>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f4f4f9;
            color: #333;
            margin: 0;
            padding: 20px;
            text-align: center;
        }
        h1, h2 {
            color: #333;
        }
        form {
            margin: 20px auto;
            padding: 20px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
            display: inline-block;
            text-align: left;
        }
        label {
            margin: 10px 0;
            display: block;
            color: #666;
        }
        input[type="range"] {
            width: 100%;
        }
        button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 20px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 16px;
            margin: 4px 2px;
            cursor: pointer;
            border-radius: 4px;
        }
        table {
            width: 80%;
            margin: 20px auto;
            border-collapse: collapse;
            text-align: center;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        th, td {
            padding: 12px;
            border: 1px solid #ccc;
            font-size: 16px;
        }
        th {
            background-color: #4CAF50;
            color: white;
        }
        #result {
            margin-top: 20px;
            background-color: #fff;
            padding: 10px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        div[id$="Plot"] {
            margin: 20px auto;
            width: 80%;
            height: 400px;
            background-color: #fff;
            padding: 10px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
    </style>

</head>
<body>
    <h1>洗衣机模糊推理系统</h1>

    <h2>输入</h2>
    <form id="fuzzyForm">
        <div>
            <label for="sludge">污泥指数 (0-100):</label>
            <input type="range" id="sludge" name="sludge" min="0" max="100" step="0.1" value="50" oninput="updateSludgeValue(this.value)">
            <span id="sludgeValue">50</span>
        </div>
        <div>
            <label for="grease">油脂指数 (0-100):</label>
            <input type="range" id="grease" name="grease" min="0" max="100" step="0.1" value="50" oninput="updateGreaseValue(this.value)">
            <span id="greaseValue">50</span>
        </div>
        <button type="button" onclick="performInference()">计算洗涤时间</button>
    </form>

    <div id="result"></div>

    <h2>隶属函数图</h2>
    <div id="membershipPlot" style="width:600px;height:400px;"></div>
    <h2>洗涤时间隶属度函数图</h2>
    <div id="washTimeMembershipPlot" style="width:600px;height:400px;"></div>

    <h2>模糊控制规则表</h2>
    <table>
        <tr>
            <th>污泥</th>
            <th>油脂</th>
            <th>洗涤时间</th>
        </tr>
        <tr>
            <td>低</td>
            <td>低</td>
            <td>非常短 (VS)</td>
        </tr>
        <tr>
            <td>低</td>
            <td>中</td>
            <td>短 (S)</td>
        </tr>
        <tr>
            <td>低</td>
            <td>高</td>
            <td>中等 (M)</td>
        </tr>
        <tr>
            <td>中</td>
            <td>低</td>
            <td>短 (S)</td>
        </tr>
        <tr>
            <td>中</td>
            <td>中</td>
            <td>中等 (M)</td>
        </tr>
        <tr>
            <td>中</td>
            <td>高</td>
            <td>长 (L)</td>
        </tr>
        <tr>
            <td>高</td>
            <td>低</td>
            <td>中等 (M)</td>
        </tr>
        <tr>
            <td>高</td>
            <td>中</td>
            <td>长 (L)</td>
        </tr>
        <tr>
            <td>高</td>
            <td>高</td>
            <td>非常长 (VL)</td>
        </tr>
    </table>


    <h2>推理结果立体图</h2>
    <div id="resultPlot" style="width:80%; height:800px; margin: 20px auto;"></div>


    <script>
        window.onresize = function() {
            plotResults();  // 当窗口大小改变时重新绘制图表
        };

        function updateSludgeValue(value) {
            document.getElementById('sludgeValue').textContent = parseFloat(value).toFixed(1);
        }

        function updateGreaseValue(value) {
            document.getElementById('greaseValue').textContent = parseFloat(value).toFixed(1);
        }


        function plotMembershipFunctions() {
            // 污泥隶属度函数
            var sludgeLow = {
                x: [0, 25, 50, 100],
                y: [1, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '污泥低'
            };
            var sludgeMedium = {
                x: [0, 25, 50, 75, 100],
                y: [0, 0, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '污泥中'
            };
            var sludgeHigh = {
                x: [0, 50, 75, 100],
                y: [0, 0, 1, 1],
                type: 'scatter',
                mode: 'lines+markers',
                name: '污泥高'
            };

            // 油脂隶属度函数
            var greaseLow = {
                x: [0, 25, 50, 100],
                y: [1, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '油脂低'
            };
            var greaseMedium = {
                x: [0, 25, 50, 75, 100],
                y: [0, 0, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '油脂中'
            };
            var greaseHigh = {
                x: [0, 50, 75, 100],
                y: [0, 0, 1, 1],
                type: 'scatter',
                mode: 'lines+markers',
                name: '油脂高'
            };

            var data = [sludgeLow, sludgeMedium, sludgeHigh, greaseLow, greaseMedium, greaseHigh];

            var layout = {
                title: '输入语言变量的隶属度',
                xaxis: {
                    title: '输入值',
                    range: [0, 100]
                },
                yaxis: {
                    title: '隶属度',
                    range: [0, 1]
                }
            };

            Plotly.newPlot('membershipPlot', data, layout);
        }


// *************************** 3D 图*************************************
        function plotResults() {
            // 定义油脂和污泥的范围
            var x = Array.from({length: 11}, (_, i) => i * 10); // 油脂指数从0到100
            var y = Array.from({length: 11}, (_, i) => i * 10); // 污泥指数从0到100

            // 计算洗涤时间
            var z = [];
            for (let i = 0; i < y.length; i++) {
                let row = [];
                for (let j = 0; j < x.length; j++) {
                    // 计算隶属度
                    let sludgeMembership = Sludge(y[i]);
                    let greaseMembership = Grease(x[j]);
                    let rulesValue = Rules(sludgeMembership, greaseMembership);
                    let timeLevel = Inference(rulesValue);
                    let predictedTime = Max_membership(timeLevel);
                    row.push(predictedTime);
                }
                z.push(row);
            }

            var data = [{
                x: x,
                y: y,
                z: z,
                type: 'surface',
                colorscale: 'Viridis',
                contours: {
                    z: {
                        show: true,
                        usecolormap: true,
                        highlightcolor: "#42f462",
                        project: { z: true }
                    }
                }
            }];

            var layout = {
                title: '推理结果立体图',
                scene: {
                    xaxis: {title: '油脂指数'},
                    yaxis: {title: '污泥指数'},
                    zaxis: {title: '洗涤时间 (分钟)'}
                },
                autosize: true,
                margin: {
                    l: 50,
                    r: 50,
                    b: 65,
                    t: 90,
                }
            };

            Plotly.newPlot('resultPlot', data, layout);
        }

        // 污泥隶属度计算
        function Sludge(a) {
            let sludge = [0, 0, 0];  // 默认隶属度为0，依次对应SD, MD, LD
            if (0 <= a && a <= 50) {
                sludge[0] = (50 - a) / 50;
                sludge[1] = a / 50;
            } else if (50 < a && a <= 100) {
                sludge[1] = (100 - a) / 50;
                sludge[2] = (a - 50) / 50;
            }
            return sludge;
        }

        // 油脂隶属度计算
        function Grease(a) {
            let grease = [0, 0, 0];  // 默认隶属度为0,依次对应NG, MG, LG
            if (0 <= a && a <= 50) {
                grease[0] = (50 - a) / 50;
                grease[1] = a / 50;
            } else if (50 < a && a <= 100) {
                grease[1] = (100 - a) / 50;
                grease[2] = (a - 50) / 50;
            }
            return grease;
        }

        // 规则前提推理，并运算取最小值
        function Rules(a, b) {
            let rulesValue = [0, 0, 0, 0, 0, 0, 0, 0, 0];  // 依次对应9条规则结果VS, M, L, S, M, L, M, L, VL
            if (a[0] !== 0 && b[0] !== 0) {
                rulesValue[0] = Math.min(a[0], b[0]);
            }
            if (a[0] !== 0 && b[1] !== 0) {
                rulesValue[1] = Math.min(a[0], b[1]);
            }
            if (a[0] !== 0 && b[2] !== 0) {
                rulesValue[2] = Math.min(a[0], b[2]);
            }
            if (a[1] !== 0 && b[0] !== 0) {
                rulesValue[3] = Math.min(a[1], b[0]);
            }
            if (a[1] !== 0 && b[1] !== 0) {
                rulesValue[4] = Math.min(a[1], b[1]);
            }
            if (a[1] !== 0 && b[2] !== 0) {
                rulesValue[5] = Math.min(a[1], b[2]);
            }
            if (a[2] !== 0 && b[0] !== 0) {
                rulesValue[6] = Math.min(a[2], b[0]);
            }
            if (a[2] !== 0 && b[1] !== 0) {
                rulesValue[7] = Math.min(a[2], b[1]);
            }
            if (a[2] !== 0 && b[2] !== 0) {
                rulesValue[8] = Math.min(a[2], b[2]);
            }
            return rulesValue;
        }

        // 每条规则推理输出
        function Inference(a) {
            let timeLevel = [0, 0, 0, 0, 0];  // 默认时间隶属值为0，依次对应VS, S, M, L, VL
            timeLevel[0] = a[0];
            timeLevel[1] = a[3];
            if (a[1] !== 0 || a[4] !== 0 || a[6] !== 0) {
                let list1 = [a[1], a[4], a[6]].filter(value => value !== 0);
                timeLevel[2] = Math.min(...list1);
            }
            if (a[2] !== 0 || a[5] !== 0 || a[7] !== 0) {
                let list2 = [a[2], a[5], a[7]].filter(value => value !== 0);
                timeLevel[3] = Math.min(...list2);
            }
            timeLevel[4] = a[8];
            return timeLevel;
        }

        // 由加权平均判决法可得，根据洗涤时间隶属函数的反函数用相应的隶属度求出时间论域中对应的值
        function Max_membership(a) {
            let time = [0, 0, 0, 0, 0, 0, 0, 0];
            time[0] = 30 - 30 * a[0];
            time[1] = 30 * a[1];
            time[2] = 60 - 30 * a[1];
            time[3] = 30 * a[2] + 30;
            time[4] = 90 - 30 * a[2];
            time[5] = 30 * a[3] + 60;
            time[6] = 120 - 30 * a[3];
            time[7] = 30 * a[4] + 90;
            let sum1 = time[0] * a[0] + time[1] * a[1] + time[2] * a[1] + time[3] * a[2] + time[4] * a[2] + time[5] * a[3] + time[6] * a[3] + time[7] * a[4];
            let sum2 = a[0] + 2 * a[1] + 2 * a[2] + 2 * a[3] + a[4];
            return sum1 / sum2;
        }

        document.addEventListener('DOMContentLoaded', function() {
            plotMembershipFunctions();
            plotResults();
            plotWashTimeMembership(); // 绘制洗涤时间隶属度函数
        });


// **************************** 3D 图 *******************************

        function calculateWashTimeMembership(a) {
            let time_level = '';
            let u1, u2, u3, u4, u5, u6, u7, u8;

            if (0 <= a && a <= 30) {
                u1 = (30 - a) / 30;
                u2 = a / 30;
                time_level = (u1 > u2) ? 'VS' : 'S';
            } else if (30 < a && a <= 60) {
                u3 = (60 - a) / 30;
                u4 = (a - 30) / 30;
                time_level = (u3 > u4) ? 'S' : 'M';
            } else if (60 < a && a <= 90) {
                u5 = (90 - a) / 30;
                u6 = (a - 60) / 30;
                time_level = (u5 > u6) ? 'M' : 'L';
            } else if (90 < a && a <= 120) {
                u7 = (120 - a) / 30;
                u8 = (a - 90) / 30;
                time_level = (u7 > u8) ? 'L' : 'VL';
            }
            return { time_level, memberships: [u1, u2, u3, u4, u5, u6, u7, u8] };
        }

        function plotWashTimeMembership() {
            // 定义洗涤时间隶属度函数，确保对称性
            var veryShort = {
                x: [0, 10, 20, 100],
                y: [1, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '非常短 (VS)'
            };
            var short = {
                x: [0, 10, 30, 50, 100],
                y: [0, 0, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '短 (S)'
            };
            var medium = {
                x: [0, 30, 50, 70, 100],
                y: [0, 0, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '中等 (M)'
            };
            var long = {
                x: [0, 50, 70, 90, 100],
                y: [0, 0, 1, 0, 0],
                type: 'scatter',
                mode: 'lines+markers',
                name: '长 (L)'
            };
            var veryLong = {
                x: [0, 80, 90, 100],
                y: [0, 0, 1, 1],
                type: 'scatter',
                mode: 'lines+markers',
                name: '非常长 (VL)'
            };

            var data = [veryShort, short, medium, long, veryLong];

            var layout = {
                title: '洗涤时间隶属度函数',
                xaxis: {
                    title: '时间 (分钟)',
                    range: [0, 100]
                },
                yaxis: {
                    title: '隶属度',
                    range: [0, 1]
                }
            };

            Plotly.newPlot('washTimeMembershipPlot', data, layout);
        }




        document.addEventListener('DOMContentLoaded', function() {
            plotMembershipFunctions();
            plotResults();
            plotWashTimeMembership(); // 绘制洗涤时间隶属度函数
        });

        function performInference() {
            const sludge = document.getElementById('sludge').value;
            const grease = document.getElementById('grease').value;
            fetch(`/fuzzy_sys/fuzzy_inference?sludge=${sludge}&grease=${grease}`)
                .then(response => response.json())
                .then(data => {
                    if (data.error) {
                        document.getElementById('result').innerHTML = '错误: ' + data.error;
                    } else {
                        document.getElementById('result').innerHTML = `模糊推理系统预测洗涤时间: ${data.time_sign} (${data.time_description}), 预计洗涤时间: ${data.predicted_time.toFixed(2)} 分钟`;
                        plotResults(); // Assuming data for the plot is fetched or updated here
                    }
                }).catch(error => {
                    document.getElementById('result').innerHTML = '请求失败: ' + error;
                });
        }

        document.addEventListener('DOMContentLoaded', function() {
            plotMembershipFunctions();
            plotResults(); // Initial plot for demonstration
        });
    </script>
</body>
</html>
